﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace org.alex.edss
{
    public sealed class EdsServer
    {
        private Socket Listenner;

        public int SessionCount;

        public int BlockQueueLength
        {
            get;
            set;
        }
        public int MaxSession
        {
            get;
            set;
        }
        public int ListenPort
        {
            get;
            set;
        }
        public int BufferSize
        {
            get;
            set;
        }

        public bool Working
        {
            get;
            set;
        }

        public event ApplicationEventHandler AppStart;

        public event ApplicationEventHandler AppEnd;

        public event ApplicationEventHandler AppError;

        public event SessionEventHandler SessionStart;

        public SessionPool Sessions
        {
            get;
            set;
        }

        public EdsServer(int Port) : this(Port, 200) { }

        public EdsServer(int Port, int Block) : this(Port, Block, 1024) { }

        public EdsServer(int Port, int Block, int Session) : this(Port, Block, Session, 4096) { }


        public EdsServer(int Port, int Block, int Session,int Buffer)
        {
            ListenPort = Port;
            BlockQueueLength = Block;
            MaxSession = Session;
            BufferSize = Buffer;
            Sessions = new SessionPool(Session, Buffer);
            Listenner = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
            Listenner.Bind(
                new IPEndPoint(
                    IPAddress.Any, ListenPort
                    )
                    );       
        }
        public void Start()
        {
            try
            {
                Working = true;
                Listenner.Listen(BlockQueueLength);
                if (AppStart != null)
                    AppStart("Application is startting");
                StartAccept(null);
            }
            catch (Exception ex)
            {
                if (AppError != null)
                {
                    AppError(ex.Message);
                }
            }
        }

        public void Stop()
        {
            Working = false;
            if (AppEnd != null)
            {
                AppEnd("Server is stopping");
            }
        }

        private void StartAccept(SocketAsyncEventArgs Args)
        {
            try
            {
                if (Working)
                {
                    if (Args == null)
                    {
                        Args = new SocketAsyncEventArgs();
                        Args.Completed += new EventHandler<SocketAsyncEventArgs>(Args_Completed);
                    }
                    else
                    {
                        Args.AcceptSocket = null;
                    }
                    if (!Listenner.AcceptAsync(Args))
                    {
                        if (AppError != null)
                        {
                            AppError("Accept error:Listenner error");
                        }
                    }
                }
                else
                {
                    if (AppEnd != null)
                    {
                        AppEnd("app stop by manul");
                    }
                    Listenner.Shutdown(SocketShutdown.Both);
                    Listenner.Close();
                }
            }
            catch (Exception ex)
            {
                if (AppError != null)
                {
                    AppError(ex.Message);
                }
            }
        }
        void Args_Completed(object sender, SocketAsyncEventArgs e)
        {
            //throw new NotImplementedException();
            ProcessAccept(e);
        }
        void ProcessAccept(SocketAsyncEventArgs e)
        {
            Session ses = null;
            try
            {
                Interlocked.Increment(ref SessionCount);
                ses = Sessions.GetSession(SessionCount);
                ses.Start(e.AcceptSocket, this);
                if (SessionStart != null)
                {
                    SessionStart(ses, 0);
                }
                StartAccept(e);
            }
            catch (Exception ex)
            {
                if (AppError != null)
                {
                    AppError(ex.Message);
                }
                if (ses != null)
                {
                    ses.Close();
                }
            }
            
        }

    }

}
